import java.util.ArrayList;






public class Team8_Player implements MancalaPlayer{
	
	private class Node{
		
		 public ArrayList<Node> children;
         public MancalaGameState gs;
         public int heuristic_value;
         public int move;
         public int player;
         public Node parent = null;
         
         public Node(MancalaGameState gs){
        	 this.gs = gs;
             children = new ArrayList<Node>();
             heuristic_value = 0;
             player = gs.CurrentPlayer;
         }
         
         
         public void generateChildren()
         {
                 MancalaGameState gs_copy = gs.copy();
                 if(player==0){
                         for(int i=-1;i<gs.cols();i++)
                     {
                             if(gs.validMove(i))
                             {
                            	
                                     try {
										gs_copy = gs_copy.play(i);
									} catch (Exception e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
                                     Node child = new Node(gs_copy);
                                     child.move = i; 
                                     child.parent = this;
                                     children.add(child);
                             }

                             gs_copy = gs.copy();
                     }
                 }
                 else{
                         for(int i=gs.cols()-1;i>-2;i--)
                     {
                        	 
                             if(gs.validMove(i))
                             {
                                     try {
										gs_copy = gs_copy.play(i);
									} catch (Exception e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
                                     Node child = new Node(gs_copy);
                                     child.move = i; 
                                     child.parent = this;
                                     children.add(child);
                             }

                             gs_copy = gs.copy();
                     }
                 }

         }


		public boolean checkEndGame() {
			return gs.checkEndGame();
		}
		
		public String toString(){
            String s ="";
            for(Node child: children){
                    s+="<"+child.move+", "+child.heuristic_value+"> ";
            }
            return s;
    }


		public int maxValue() {
			int max = Integer.MIN_VALUE;
			for(Node child: children){
               max = Math.max(max, child.heuristic_value);
			}
			
			if(max==Integer.MIN_VALUE){
				return 0;
			}
			
			return max;
		}


		public int minValue() {
			int min = Integer.MAX_VALUE;
			for(Node child: children){
               min = Math.min(min, child.heuristic_value);
			}
			
			if(min==Integer.MAX_VALUE){
				return 0;
			}
			return min;
		}
	}
	
	
	
		int player;
	 private int score;
     private WeightCollection myWeights;
     public int gamesPlayed;
     int[] temp = new int[5];
	public Team8_Player(int player){
		this.player = player;
	}
	
	public Team8_Player(int player_num, WeightCollection weights)
    {
            player = player_num;
            myWeights = weights;
            score = 0;
            gamesPlayed = 0;
    }
	
	
	public int alphaBeta(Node gameNode, int alpha, int beta, int depth){

		if(gameNode.checkEndGame()){
			return evaluate(gameNode.gs);
		}
		
		
		gameNode.generateChildren();
		for(Node child: gameNode.children){
			child.heuristic_value = evaluate(child.gs);
		}
	
		
		if(depth<=0 || gameNode.checkEndGame()){
			if(gameNode.player == this.player){
				return gameNode.maxValue();
			}
			else{
				return gameNode.minValue();
			}
		}

		 if(gameNode.player == this.player)
         {
                 for(Node child : gameNode.children)
                 {
                	 //alpha = child.heuristic_value;
                		child.heuristic_value = alphaBeta(child,alpha,beta,depth-1);
                		alpha = child.heuristic_value;
                         if(beta<=alpha)
                         {
                                 break;
                         }


                 }
         return gameNode.maxValue();
         }
         else
         {
                 for(Node child : gameNode.children)
                 {
                	// beta = child.heuristic_value;
                	 	child.heuristic_value = alphaBeta(child,alpha,beta,depth-1);
                	 	beta = child.heuristic_value;
                         if(beta<=alpha)
                         {
                                 break;
                         }

                 }
                 return gameNode.minValue();
         }
	}
	
	

	 public int evaluate(MancalaGameState gs)
     {
     	 MancalaGameState gs_copy = gs.copy();
          int sd = ScoreDifference(gs_copy);
          int sic = StoneInControl(gs_copy);
          int matc = MaxAmountToCapture(gs_copy);
          int maltc = MaxAmountLoseToCapture(gs_copy);
          int value = 0;
          
             if(gs_copy.checkEndGame()){
                 gs_copy.computeFinalScore();
                     if (gs_copy.getScore(player) > gs_copy.getScore(1-player)) {
                         value += 1000;   
                     }
                     else
                      value += -1000;
                }

             if(myWeights == null)
             {
                     //preset weights

                     value +=  (30*sd+(sic*11)+(matc*40)-(maltc*100));
            	 
                  //   value +=  30*sd+(sic*11);
             }
             else
             {
                     /* Needs to be replaced with heuristic corresponding to weights! */
            	 value +=  (myWeights.getWeight(0)*sd+(sic*myWeights.getWeight(1))+(matc*myWeights.getWeight(2))-(maltc*myWeights.getWeight(3)));
            //	 value +=  myWeights.getWeight(0)*sd+(sic*myWeights.getWeight(1));
             }
             return value;

     }       
     
     private int MaxAmountLoseToCapture(MancalaGameState gs) {
             int max = 0;

             for(int i = 0; i < 6; i++)
             {
                     
                     if(gs.stonesAt(1-player, i) == 0 && gs.stonesAt(player, i)>0 )
                     {
                             
                             int myStone = gs.stonesAt(player, i);
                             int move = findReachableMoveToBucket(gs,i,1-player);
                             if(move!=-1)
                             {
                                     if(myStone > max)
                                     {       
                                             max = myStone;
                                     }
                                     /* use to debug whether it finds a buckt that can be captured
                                     System.out.println("WARNING: There is an opening of "+myStone+" stones to lose at bucket "+i+" with move "+move);
                                     gs.printState();
                                     */
                             }
                                     
                     }
             }
             
             return max;
     }

     private int MaxAmountToCapture(MancalaGameState gs) {
             int max = 0;

             for(int i = 0; i < 6; i++)
             {
                     
                     if(gs.stonesAt(player, i) == 0 && gs.stonesAt(1-player, i)>0 )
                     {
                             
                             int opponentStone = gs.stonesAt(1-player, i);
                             int move = findReachableMoveToBucket(gs,i,player);
                             if(move!=-1)
                             {
                                     if(opponentStone > max)
                                     {       
                                             max = opponentStone;
                                     }
                                     /* use to debug whether it finds a buckt that can be captured
                                     System.out.println("There is an opening of "+opponentStone+" stones to capture at bucket "+i+" with move "+move);
                                     gs.printState();
                                     */
                             }
                                     
                     }
             }
             
             return max;
     }
     
       

     private int findReachableMoveToBucket(MancalaGameState gs, int bucket, int player) {
             for(int i=0;i<gs.cols();i++)
             {
                     int stone = gs.stonesAt(player, i);
                     if(gs.validMove(i)&&player==0){
                             if(i<bucket){
                                     if((bucket-i)==stone){
                                             return i;
                                     }
                             }
                             if(i>bucket){
                                     if((13-stone)==(bucket-i)){
                                             return i;
                                     }
                             }
                     }
                     if(gs.validMove(i)&&player==1){
                             if(i>bucket){
                                     if((bucket-i)==stone){
                                             return i;
                                     }
                             }
                             if(i<bucket){
                                     if((13-stone)==(bucket-i)){
                                             return i;
                                     }
                             }
                     }
             }
             return -1;
     }

     public int ScoreDifference(MancalaGameState gs)
     {
             return  gs.getScore(player)-gs.getScore(1-player);
     }
     
     public int StoneInControl(MancalaGameState gs)
     {
             int number = 0;
             
             for(int i=0;i<gs.cols();i++)
             {
                     number+= gs.stonesAt(player,i)-gs.stonesAt(1-player,i);
             }
             
             return number;
     }


	@Override
	public int getMove(MancalaGameState gs) throws Exception {
		  int move = -1;
		  Node root = new Node(gs);
		  
		  if(gs.getScore(player)==1&&gs.getScore(1-player)==0){
			  return 0;
		  }
		  
		  
		  alphaBeta(root,Integer.MIN_VALUE,Integer.MAX_VALUE,7);
		  
	//	  System.out.println(root.toString());
		
		  int max = Integer.MIN_VALUE;
		  for(Node child: root.children){
			  if(max<=child.heuristic_value){
				  max = child.heuristic_value;
				  move = child.move;
			  }
		  }
		  return move;
	}
	  //performs post game actions.
    @Override
    public Object postGameActions(MancalaGameState gs) {
            // TODO Auto-generated method stub
            return null;
    }

    public int getScore() { return score; }
    public void resetScore() { score = 0;}
    public void addWin() {score = score +2;}
    public void addDraw() {score = score +1;}
    
    public WeightCollection getWeights() {return myWeights;}
    public WeightCollection returnMyWeights(){return myWeights;}
            @Override
            public Object actionsBeforeDeletion() {
                    // TODO Auto-generated method stub
                    return null;
            }
    
            

}
